package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"text/template"

	"nggs/util"

	"nggs/tools/print"
	"nggs/tools/static_data"
	"nggs/tools/static_data/json"
)

var (
	flagConfigFilePath = flag.String("cfg", "./gen_static_data_code.json", "config file path, default=./gen_static_data_code.json")

	gExtendBeginRegex *regexp.Regexp
)

type staticData struct {
	Package string
	Tables  []*json.Table
	Enums   []*static_data.Enum
}

func main() {
	flag.Parse()

	cfg, err := loadConfig(*flagConfigFilePath)
	if err != nil {
		panic(err)
	}

	gExtendBeginRegex, err = regexp.Compile(`//<\w+?>`)
	if err != nil {
		panic(err)
	}

	util.MustMkdirIfNotExist(cfg.SourceCodeDir)

	tpl, err := template.New("gen_static_data_code").Funcs(gTemplateFuncMap).ParseFiles(
		filepath.Join(cfg.TemplateDir, "table.gohtml"),
		filepath.Join(cfg.TemplateDir, "global.gohtml"),
		filepath.Join(cfg.TemplateDir, "enum.gohtml"),
	)
	if err != nil {
		panic(err)
	}

	if cfg.Debug {
		print.SetDebugMode(true)
	}

	sd := json.New(cfg.Package)

	if cfg.EnumFilePath != "" {
		sd.Enums, err = static_data.ReadEnum(cfg.EnumFilePath)
		if err != nil {
			panic(err)
			return
		}
	}

	if len(sd.Enums) > 0 {
		fileName := fmt.Sprintf("enums.%s", cfg.Extend)
		filePath := filepath.Join(cfg.SourceCodeDir, fileName)
		filePath, _ = filepath.Abs(filePath)

		err = sd.CollectExtend(filePath, gExtendBeginRegex)
		if err != nil {
			print.Errorf("[%s]收集扩展代码失败, %s", fileName, err)
			return
		}

		var buffer bytes.Buffer
		err = tpl.ExecuteTemplate(&buffer, "enum.gohtml", sd)
		if err != nil {
			print.Errorf("生成[%s]失败, %s", filePath, err)
			return
		}

		bs := buffer.Bytes()

		if cfg.Format {
			bs, err = format.Source(buffer.Bytes())
			if err != nil {
				print.Errorf("生成[%s]失败, %s\n%s", filePath, err, string(bs))
				return
			}
		}

		err = ioutil.WriteFile(filePath, bs, 0766)
		if err != nil {
			print.Errorf("写入[%s]失败, %s", filePath, err)
			return
		}

		print.Infof("-> %s", filePath)
	}

	err = util.Walk(cfg.JsonDir, "json", cfg.WhiteList, cfg.BlackList, func(path string, info os.FileInfo, name string, isInBlackList bool) bool {
		if isInBlackList {
			print.Infof("[%s]在黑名单中，跳过", path)
			return true
		}

		print.Infof("<- %s", path)

		tbl, err := json.ReadJsonFile(path, info, sd.Enums, cfg.Package, cfg.ReplaceNames)
		if err != nil {
			print.Errorf("读取[%s]失败，%s", path, err)
			return true
		}

		fileName := fmt.Sprintf("%s.%s", tbl.RawName, cfg.Extend)
		filePath := filepath.Join(cfg.SourceCodeDir, fileName)
		filePath, _ = filepath.Abs(filePath)

		err = tbl.CollectExtend(filePath, gExtendBeginRegex)
		if err != nil {
			print.Errorf("[%s]收集扩展代码失败, %s", fileName, err)
			return true
		}

		var buffer bytes.Buffer
		err = tpl.ExecuteTemplate(&buffer, "table.gohtml", tbl)
		if err != nil {
			print.Errorf("生成[%s]失败, %s", fileName, err)
			return true
		}

		bs := buffer.Bytes()

		if cfg.Format {
			bs, err = format.Source(buffer.Bytes())
			if err != nil {
				print.Errorf("格式化代码失败, %s\n%s", err, buffer.Bytes())
				return true
			}
		}

		err = ioutil.WriteFile(filePath, bs, 0766)
		if err != nil {
			print.Errorf("生成[%s]的[%s]失败, %s", tbl.JsonFileName, fileName, err)
			return true
		}

		print.Infof("-> %s", filePath)

		sd.Tables = append(sd.Tables, tbl)

		return true
	})
	if err != nil {
		panic(err)
	}
	{
		fileName := fmt.Sprintf("static_data.%s", cfg.Extend)
		filePath := filepath.Join(cfg.SourceCodeDir, fileName)
		filePath, _ = filepath.Abs(filePath)

		err = sd.CollectExtend(filePath, gExtendBeginRegex)
		if err != nil {
			print.Errorf("[%s]收集扩展代码失败, %s", fileName, err)
			return
		}

		var buffer bytes.Buffer
		err = tpl.ExecuteTemplate(&buffer, "global.gohtml", sd)
		if err != nil {
			print.Errorf("生成[%s]失败, %s", filePath, err)
			return
		}

		bs := buffer.Bytes()
		if cfg.Format {
			bs, err = format.Source(buffer.Bytes())
			if err != nil {
				print.Errorf("生成[%s]失败, %s\n%s", filePath, err, string(bs))
				return
			}
		}

		err = ioutil.WriteFile(filePath, bs, 0766)
		if err != nil {
			print.Errorf("写入[%s]失败, %s", filePath, err)
			return
		}

		print.Infof("-> %s", filePath)
	}
}
